////////////////////////////////////////////////////////////////////////////
//
//  Crytek Engine Source File.
//  Copyright (C), Crytek Studios, 2002.
// -------------------------------------------------------------------------
//  File name:   statobjrend.cpp
//  Version:     v1.00
//  Created:     28/5/2001 by Vladimir Kajalin
//  Compilers:   Visual Studio.NET
//  Description: prepare and add render element into renderer
// -------------------------------------------------------------------------
//  History:
//
////////////////////////////////////////////////////////////////////////////

#include "StdAfx.h"

#include "StatObj.h"
#include "../RenderDll/Common/Shadow_Renderer.h"

#include "IndexedMesh.h"
#include "VisAreas.h"
#include "GeomQuery.h"
#include "RenderObjectDefines.h"

void CStatObj::SetupBending(CRenderObject*& pObj, const IRenderNode* pNode, bool alreadyDuplicated)
{
	FUNCTION_PROFILER_3DENGINE;
  if (!GetCVars()->e_VegetationBending)
		return;
	if (!pNode->m_pRNTmpData)
		return;

	static const float
		fBEND_RESPONSE					= 0.0005f,
		fBEND_MAX								= 2.f,
		fAMP_PARALLEL						= 0.4f,
		fAMP_TRANSVERSE					= 0.1f,
		fFREQ_PARALLEL					= 1.f,
		fFREQ_TRANSVERSE_SCALE	= 1.2f,
		fFREQ_MIN								= 0.1f,
		fHEIGHT_MIN							= 1.f,
		fINTERP_RATE						= 0.5f,
		fSPATIAL_FREQ						= 0.125f;

	CRNTmpData::SRNUserData& userData = pNode->m_pRNTmpData->userData;

	if (userData.m_fMainBendingScale == 0.f)
	{
		pObj->m_ObjFlags &= ~FOB_BENDED;
		return;
	}

	if (userData.nBendingLastFrame != (GetMainFrameID()&~(3<<29)))
	{
		userData.nBendingLastFrame = GetMainFrameID();

		const Vec3 &vObjPos = pObj->GetTranslation();

		if (!userData.bWindCurrent)
		{
			userData.vCurrentWind = m_p3DEngine->GetWind(pNode->GetBBox(), !!pNode->GetEntityVisArea());
			userData.bWindCurrent = true;
		}

		// Soft clamp bending from wind amplitude.
		Vec2 vBending = Vec2(userData.vCurrentWind) * fBEND_RESPONSE * userData.m_fMainBendingScale;
		vBending *= fBEND_MAX / (fBEND_MAX + vBending.GetLength());

		SBending* pBending = &userData.m_Bending;

		SWaveForm2& wave0 = pBending->m_Waves[0];
		SWaveForm2& wave1 = pBending->m_Waves[1];

		if (!userData.bBendingSet)
		{
			// First time shown, set full bending.
			userData.bBendingSet = true;

			float fWaveFreq = fFREQ_PARALLEL / (m_fRadiusVert+fHEIGHT_MIN) + fFREQ_MIN;

			wave0.m_Freq			= fWaveFreq;
			wave0.m_Phase			= vObjPos.x * fSPATIAL_FREQ;
			wave0.m_eWFType		= eWF_Sin;

			wave1.m_Freq			= fWaveFreq * fFREQ_TRANSVERSE_SCALE;
			wave1.m_Phase			= vObjPos.y * fSPATIAL_FREQ;
			wave1.m_eWFType		= eWF_Sin;
		}
		else
		{
			// Already visible, fade toward current value.
			float fInterp = min(gEnv->pTimer->GetFrameTime() * wave0.m_Freq * fINTERP_RATE, 1.f);
			vBending.x += (wave0.m_Level - vBending.x) * (1.f - fInterp);
			vBending.y += (wave1.m_Level - vBending.y) * (1.f - fInterp);
		}

		wave0.m_Level		 = vBending.x;
		wave0.m_Amp			 = fabs_tpl(vBending.x) * fAMP_PARALLEL + fabs_tpl(vBending.y) * fAMP_TRANSVERSE;

		wave1.m_Level		 = vBending.y;
		wave1.m_Amp			 = fabs_tpl(vBending.y) * fAMP_PARALLEL + fabs_tpl(vBending.x) * fAMP_TRANSVERSE;
	}

	if (!alreadyDuplicated) 
		pObj = GetRenderer()->EF_DuplicateRO(pObj, NULL);
	SRenderObjData* pOD = GetRenderer()->EF_GetObjData(pObj, true);
	if (!pOD) 
		return;

	pObj->m_ObjFlags |= FOB_BENDED | FOB_VEGETATION;  
	pOD->m_pBending = Get3DEngine()->GetBendingEntry(&userData.m_Bending); 
}

void CStatObj::Render(const SRendParams & rParams)
{
  FUNCTION_PROFILER_3DENGINE;

  if (m_nFlags & STATIC_OBJECT_HIDDEN)
    return;

#ifndef _RELEASE
	int nMaxDrawCalls=GetCVars()->e_MaxDrawCalls;
	if (nMaxDrawCalls>0)
	{
		// Don't calculate the number of drawcalls every single time a statobj is rendered.		
		// This creates a flickering effect with objects appearing and disappearing indicating that the limit has been reached.
		static int nCurrObjCounter=0;
		if (((nCurrObjCounter++)&31)==1)
		{						
			if (GetRenderer()->GetCurrentNumberOfDrawCalls()>nMaxDrawCalls)
				return;
		}			
	}
#endif // _RELEASE	

	if(m_pIndexedMesh && m_pIndexedMesh->m_bInvalidated)
	{
		MakeRenderMesh();
		m_pIndexedMesh->m_bInvalidated = false;
	}

  CRenderObject * pObj = GetRenderer()->EF_GetObject_Temp();
  SRenderObjectModifier rom;
  FillRenderObject(rParams, rParams.pRenderNode, m_pMaterial, NULL, pObj, &rom);

  Matrix34 m34;

  if(rom.nMatricesInUse) 
    m34 = rom.mat;
  else
    m34 = pObj->m_II.m_Matrix;

	int nNewLod = rParams.nLod;
/*  int nNewLod = ComputeLod(m34, pObj->m_fDistance, rParams.pRenderNode, false);
  if (Get3DEngine()->IsRenderingIntoShadowMap())
  {
    if (rParams.pRenderNode->GetDrawFrame(0)<Cry3DEngineBase::GetFrameID()-10)
      nNewLod += GetCVars()->e_ShadowsLodBiasInvis;
		if(rParams.pRenderNode->GetRenderNodeType() == eERType_RenderProxy)
	    nNewLod += GetCVars()->e_ShadowsLodBiasFixed;
  }*/

  return RenderInternal(pObj, rom.InUse() ? &rom : NULL, rParams.nSubObjHideMask, -1, nNewLod, rParams.nDissolveRef, -1, 255);
}

void CStatObj::RenderSubObject(CRenderObject * pRenderObject, const SRenderObjectModifier * pROM, int nThreadId, int nLod, 
															 int nSubObjId, const Matrix34A & renderTM)
{
  const SSubObject& subObj = m_subObjects[nSubObjId];

  CStatObj * const __restrict pStatObj = (CStatObj *)subObj.pStatObj;

	if( pStatObj == NULL ) 
		return;

	SRenderObjData *pOD = 0;
	if (subObj.pFoliage)
	{
		CRenderObject *pRenderObjectOrg = pRenderObject;
		(pRenderObject = GetRenderer()->EF_GetObject_Temp())->CloneObject(pRenderObjectOrg);
		pRenderObject->SetObjDataId(-1);
		pOD = GetRenderer()->EF_GetObjData(pRenderObject, true);
		pOD->m_pCharInstance = subObj.pFoliage;
		pOD->m_pInstance = subObj.pFoliage;
		pRenderObject->m_ObjFlags |= FOB_CHARACTER|FOB_VEGETATION;
		((CStatObjFoliage*)subObj.pFoliage)->m_pRenderObject = pRenderObject;
	}

  if (subObj.bIdentityMatrix && !subObj.pWeights)
  {
    pStatObj->RenderSubObjectInternal(pRenderObject, pROM, nThreadId, nLod );
  }
  else
  {
		const Matrix34 mat = renderTM * subObj.tm;
		const SRenderObjectModifier instInfo(pROM, mat, mat, subObj.pWeights, (uint8) nSubObjId);
    pStatObj->RenderSubObjectInternal(pRenderObject, &instInfo, nThreadId, nLod );
  }
}

void CStatObj::RenderStreamingDebugInfo(CRenderObject * pRenderObject)
{
#ifndef _RELEASE
//	CStatObj * pStreamable = m_pParentObject ? m_pParentObject : this;

	CStatObj * pStreamable = m_pLod0 ? m_pLod0 : this;

	int nKB = 0;
	
	if(pStreamable->m_pRenderMesh)
		nKB += pStreamable->m_nRenderMeshMemoryUsage;

	for(int nLod = 1; pStreamable->m_pLODs && nLod<MAX_STATOBJ_LODS_NUM; nLod++)
	{
		CStatObj * pLod = (CStatObj*)pStreamable->m_pLODs[nLod];

		if(!pLod)
			continue;

		if(pLod->m_pRenderMesh)
			nKB += pLod->m_nRenderMeshMemoryUsage;
	}

	nKB >>= 10;

	if( nKB > GetCVars()->e_StreamCgfDebugMinObjSize )
	{
//		nKB = GetStreamableContentMemoryUsage(true) >> 10;

		char * pComment = 0;

		pStreamable = pStreamable->m_pParentObject ? pStreamable->m_pParentObject : pStreamable;

		if(!pStreamable->m_bCanUnload)
			pComment = "No stream";
		else if(!pStreamable->m_bLodsAreLoadedFromSeparateFile && pStreamable->m_nLoadedLodsNum) 
			pComment = "Single";
		else if(pStreamable->m_nLoadedLodsNum>1) 
			pComment = "Split";
		else 
			pComment = "No LODs";

		int nDiff = SATURATEB(int(float(nKB - GetCVars()->e_StreamCgfDebugMinObjSize)/max((int)1, GetCVars()->e_StreamCgfDebugMinObjSize)*255));
		DrawBBoxLabeled( AABB(m_vBoxMin,m_vBoxMax), pRenderObject->m_II.m_Matrix, ColorB(nDiff, 255-nDiff, 0, 255),
			"%.2f mb, %s", 1.f/1024.f*(float)nKB, pComment );
	}
#endif //_RELEASE
}

//////////////////////////////////////////////////////////////////////
void CStatObj::RenderCoverInfo(CRenderObject * pRenderObject)
{
	for (int i = 0; i < GetSubObjectCount(); ++i)
	{
		const IStatObj::SSubObject* subObject = GetSubObject(i);
		if (subObject->nType != STATIC_SUB_OBJECT_DUMMY)
			continue;
		if (strstr(subObject->name, "$cover") == 0)
			continue;

		Vec3 localBoxMin = -subObject->helperSize * 0.5f;
		Vec3 localBoxMax = subObject->helperSize * 0.5f;

		GetRenderer()->GetIRenderAuxGeom()->DrawAABB(
			AABB(localBoxMin, localBoxMax),
			pRenderObject->m_II.m_Matrix * subObject->localTM,
			true, ColorB(192, 0, 255, 255),
			eBBD_Faceted);
	}
}

//////////////////////////////////////////////////////////////////////
void CStatObj::RenderInternal( CRenderObject * pRenderObject, const SRenderObjectModifier * pROM, uint64 nSubObjectHideMask, int nThreadId, int nLodA, uint8 uLodADissolveRef, int nLodB, uint8 uLodBDissolveRef)
{
  FUNCTION_PROFILER_3DENGINE;

	if (m_nFlags & STATIC_OBJECT_HIDDEN)
		return;

  m_nLastDrawMainFrameId = GetMainFrameID();
  if(m_pParentObject)
    m_pParentObject->m_nLastDrawMainFrameId = GetMainFrameID();

	if (m_nInitialSubObjHideMask != 0 && nSubObjectHideMask == 0)
	{
		nSubObjectHideMask = m_nInitialSubObjHideMask;
		if ((m_pMergedRenderMesh != NULL) && !(pRenderObject->m_ObjFlags & FOB_MESH_SUBSET_INDICES)) // If not already set by per-instance hide mask.
		{
			SRenderObjData *pOD = GetRenderer()->EF_GetObjData(pRenderObject,true);
			// Only pass SubObject hide mask for merged objects, because they have a correct correlation between Hide Mask and Render Chunks.
			pOD->m_nSubObjHideMask = m_nInitialSubObjHideMask;
			pRenderObject->m_ObjFlags |= FOB_MESH_SUBSET_INDICES;
		}
	}

	if (pRenderObject->m_pRenderNode)
	{
		IRenderNode *pRN = (IRenderNode *)pRenderObject->m_pRenderNode;
		if(m_bEditor)
		{
			if(pRN->m_dwRndFlags & ERF_SELECTED)
      {
        m_nSelectedFrameId = GetMainFrameID();
        if(m_pParentObject)
          m_pParentObject->m_nSelectedFrameId = GetMainFrameID();
				pRenderObject->m_ObjFlags |= FOB_SELECTED;
      }
			else
				pRenderObject->m_ObjFlags &= ~FOB_SELECTED;  

			if (!gEnv->IsEditing() && pRN->m_dwRndFlags & ERF_RAYCAST_PROXY)
			{
				return;
			}
		}
		else
		{
			if (pRN->m_dwRndFlags & ERF_RAYCAST_PROXY)
			{
				return;
			}
		}
  }

	if ((m_nFlags & STATIC_OBJECT_COMPOUND) && !m_bMerged)
	{
			//////////////////////////////////////////////////////////////////////////
			// Render SubMeshes if present.
			//////////////////////////////////////////////////////////////////////////
			if (m_nSubObjectMeshCount > 0)
			{
				CRenderObject *pRenderObjectB = NULL;

				if(uLodBDissolveRef != 255)
				{
					pRenderObject->m_DissolveRef = uLodADissolveRef;

					pRenderObject->m_nLod = nLodA;

					if (pRenderObject->m_DissolveRef)
					{
						if(!(pRenderObject->m_ObjFlags & FOB_DISSOLVE))
							pRenderObject->m_ObjFlags &= ~FOB_UPDATED_RTMASK;
						pRenderObject->m_ObjFlags |= FOB_DISSOLVE;
						
						pRenderObject->m_ObjFlags |= FOB_DISSOLVE_OUT;
					}
					else
					{
						if((pRenderObject->m_ObjFlags & FOB_DISSOLVE))
							pRenderObject->m_ObjFlags &= ~FOB_UPDATED_RTMASK;
						pRenderObject->m_ObjFlags &= ~FOB_DISSOLVE;
					}

					if(nLodB != -1)
					{
						pRenderObjectB = GetRenderer()->EF_DuplicateRO(pRenderObject, pROM);
						pRenderObjectB->m_nLod = nLodB;
						pRenderObjectB->m_ObjFlags &= ~FOB_DISSOLVE_OUT;
					}
				}
				else
				{
					pRenderObject->m_DissolveRef = 0;
					pRenderObject->m_nLod = nLodA;
					if((pRenderObject->m_ObjFlags & FOB_DISSOLVE))
						pRenderObject->m_ObjFlags &= ~FOB_UPDATED_RTMASK;
					pRenderObject->m_ObjFlags &= ~(FOB_DISSOLVE|FOB_DISSOLVE_OUT);
				}

				uint64 nBitIndex = 1;
				Matrix34A renderTM = pRenderObject->m_II.m_Matrix;
				for (int32 i = 0, subObjectsSize=m_subObjects.size(); i < subObjectsSize; ++i,nBitIndex <<= 1)
				{
					const SSubObject& subObj = m_subObjects[i];
					if(subObj.nType == STATIC_SUB_OBJECT_MESH) // all the meshes are at the beginning of the array.
					{
						CStatObj * const __restrict pStatObj = (CStatObj *)subObj.pStatObj;

						if(pStatObj &&
							(pStatObj->m_nRenderTrisCount >= 2) && 
							!(pStatObj->m_nFlags & STATIC_OBJECT_HIDDEN) &&
							(nBitIndex & nSubObjectHideMask) == 0
							)
						{							
							PrefetchLine(pRenderObject, 0);
							RenderSubObject(pRenderObject, pROM, nThreadId, nLodA, i, renderTM);
							if(pRenderObjectB)
							{
								PrefetchLine(pRenderObjectB, 0);
								RenderSubObject(pRenderObjectB, pROM, nThreadId, nLodB, i, renderTM);
							}
						}
					}
					else
					{
						break;
					}
				}

			if (GetCVars()->e_DebugDraw)
			{
				RenderDebugInfo( pRenderObject, pROM );
			}
		}

		//////////////////////////////////////////////////////////////////////////
	}
  else
  { // draw mesh, don't even try to render childs
		RenderObjectInternal(pRenderObject, pROM, nThreadId, nLodA, uLodADissolveRef, true);
		if(uLodBDissolveRef != 255) // check here since we're passing in A's ref.
			RenderObjectInternal(pRenderObject, pROM, nThreadId, nLodB, uLodADissolveRef, false);
	}
}

void CStatObj::RenderObjectInternal(CRenderObject * pRenderObject, const SRenderObjectModifier * pROM, int nThreadId, int nTargetLod, uint8 uLodDissolveRef, bool dissolveOut)
{
	if (nTargetLod == -1 || uLodDissolveRef == 255)
	{
		return;
	}

    int nLod = CLAMP(nTargetLod, GetMinUsableLod(), (int)m_nMaxUsableLod);
    assert(nLod<MAX_STATOBJ_LODS_NUM);

	pRenderObject = GetRenderer()->EF_DuplicateRO(pRenderObject, pROM);
	pRenderObject->m_DissolveRef = uLodDissolveRef;

	pRenderObject->m_nLod = nTargetLod;

	if (pRenderObject->m_DissolveRef)
	{
		if(!(pRenderObject->m_ObjFlags & FOB_DISSOLVE))
			pRenderObject->m_ObjFlags &= ~FOB_UPDATED_RTMASK;
		pRenderObject->m_ObjFlags |= FOB_DISSOLVE;

		if(dissolveOut)
			pRenderObject->m_ObjFlags |= FOB_DISSOLVE_OUT;
	}
	else
	{
		if((pRenderObject->m_ObjFlags & FOB_DISSOLVE))
			pRenderObject->m_ObjFlags &= ~FOB_UPDATED_RTMASK;
		pRenderObject->m_ObjFlags &= ~FOB_DISSOLVE;
	}

	// try next lod's if selected one is not ready
	if((!nLod && m_pRenderMesh && m_pRenderMesh->CanRender()) || !GetCVars()->e_Lods)
	{
		PrefetchLine(pRenderObject, 0);
		RenderRenderMesh(pRenderObject, NULL, nThreadId, pROM);
	}
	else
	{
		if(m_pLODs && m_pLODs[nLod])
		{
			m_pLODs[nLod]->m_nLastDrawMainFrameId = GetMainFrameID();
			if(m_pLODs[nLod]->m_pParentObject)
				m_pLODs[nLod]->m_pParentObject->m_nLastDrawMainFrameId = GetMainFrameID();

			if((nLod+1) < MAX_STATOBJ_LODS_NUM && m_pLODs[nLod+1])
			{
				m_pLODs[nLod+1]->m_nLastDrawMainFrameId = GetMainFrameID();
				if(m_pLODs[nLod+1]->m_pParentObject)
					m_pLODs[nLod+1]->m_pParentObject->m_nLastDrawMainFrameId = GetMainFrameID();
			}
		}

		if(m_pLODs)
			for(; nLod<=(int)m_nMaxUsableLod; nLod++)
			{
				if(m_pLODs[nLod] && m_pLODs[nLod]->m_pRenderMesh && m_pLODs[nLod]->m_pRenderMesh->CanRender())
				{
					PrefetchLine(pRenderObject, 0);
					m_pLODs[nLod]->RenderRenderMesh(pRenderObject, NULL, nThreadId, pROM);
					break;
				}
			}
	}
}

void CStatObj::RenderSubObjectInternal(CRenderObject * pRenderObject, const SRenderObjectModifier * pROM, int nThreadId, int nLod)
{
  assert(!(m_nFlags & STATIC_OBJECT_HIDDEN));

  assert(m_nRenderTrisCount);

  m_nLastDrawMainFrameId = GetMainFrameID();
  if(m_pParentObject && (m_nFlags&STATIC_OBJECT_MULTIPLE_PARENTS))
    m_pParentObject->m_nLastDrawMainFrameId = GetMainFrameID();

  assert(!m_pParentObject || m_pParentObject->m_nLastDrawMainFrameId == GetMainFrameID());

  assert(!(m_nFlags & STATIC_OBJECT_COMPOUND));

  nLod = CLAMP(nLod, GetMinUsableLod(), (int)m_nMaxUsableLod);
  assert(nLod<MAX_STATOBJ_LODS_NUM);

  // try next lod's if selected one is not ready
  if((!nLod && m_pRenderMesh && m_pRenderMesh->CanRender()) || !GetCVars()->e_Lods)
  {
		PrefetchLine(pRenderObject, 0);
    RenderRenderMesh(pRenderObject, NULL, nThreadId, pROM);
  }
  else
  {
    if(m_pLODs && m_pLODs[nLod])
    {
      m_pLODs[nLod]->m_nLastDrawMainFrameId = GetMainFrameID();
      if(m_pLODs[nLod]->m_pParentObject)
        m_pLODs[nLod]->m_pParentObject->m_nLastDrawMainFrameId = GetMainFrameID();

      if((nLod+1) < MAX_STATOBJ_LODS_NUM && m_pLODs[nLod+1])
      {
        m_pLODs[nLod+1]->m_nLastDrawMainFrameId = GetMainFrameID();
        if(m_pLODs[nLod+1]->m_pParentObject)
          m_pLODs[nLod+1]->m_pParentObject->m_nLastDrawMainFrameId = GetMainFrameID();
      }
    }

    if(m_pLODs)
      for(; nLod<=(int)m_nMaxUsableLod; nLod++)
    {
      if(m_pLODs[nLod] && m_pLODs[nLod]->m_pRenderMesh && m_pLODs[nLod]->m_pRenderMesh->CanRender())
      {
				PrefetchLine(pRenderObject, 0);
        m_pLODs[nLod]->RenderRenderMesh(pRenderObject, NULL, nThreadId, pROM);
        break;
      }
    }
  }
}

void CStatObj::FillRenderObject(const SRendParams & rParams, IRenderNode * pRenderNode, IMaterial * pMaterial, 
  SInstancingInfo * pInstInfo, CRenderObject * pObj, SRenderObjectModifier * pROM)
{
  //  FUNCTION_PROFILER_3DENGINE;

  ////////////////////////////////////////////////////////////////////////////////////////////////////
  // Specify transformation
  ////////////////////////////////////////////////////////////////////////////////////////////////////

  IRenderer * pRend = GetRenderer();

  assert(pObj);
  if (!pObj)
    return;

  pObj->m_pRenderNode = pRenderNode;
  pObj->m_fSort = rParams.fCustomSortOffset;
  SRenderObjData *pOD = NULL;
  if (rParams.pFoliage || rParams.pInstance || rParams.m_pVisArea || pInstInfo || rParams.nVisionParams || rParams.nHUDSilhouettesParams || 
			rParams.pLayerEffectParams || rParams.nSubObjHideMask)
  {
    pOD = pRend->EF_GetObjData(pObj, true);
    pOD->m_pCharInstance = rParams.pFoliage;
    if (pOD->m_pCharInstance)
      pObj->m_ObjFlags |= FOB_CHARACTER;

    //if (pInstInfo)
    //  pOD->m_pInstancingInfo = &pInstInfo->arrMats;

		pOD->m_pInstance = rParams.pInstance;
		pOD->m_pLayerEffectParams = rParams.pLayerEffectParams;
		pOD->m_nVisionParams = rParams.nVisionParams;
		pOD->m_nHUDSilhouetteParams = rParams.nHUDSilhouettesParams;

		if (rParams.nSubObjHideMask && (m_pMergedRenderMesh != NULL))
		{
			// Only pass SubObject hide mask for merged objects, because they have a correct correlation between Hide Mask and Render Chunks.
			pOD->m_nSubObjHideMask = rParams.nSubObjHideMask;
			pObj->m_ObjFlags |= FOB_MESH_SUBSET_INDICES;
		}
  }

	

  ////////////////////////////////////////////////////////////////////////////////////////////////////
  // Set flags
  ////////////////////////////////////////////////////////////////////////////////////////////////////

  pObj->m_ObjFlags |= FOB_TRANS_MASK;
  pObj->m_ObjFlags |= rParams.dwFObjFlags;
//  SRenderObjData *pObjData = NULL;
	
	if( rParams.nTextureID >= 0 )
		pObj->m_nTextureID = rParams.nTextureID;

  assert(rParams.pMatrix);
  {
    pObj->m_II.m_Matrix = *rParams.pMatrix;  
  }

  pObj->m_II.m_AmbColor = rParams.AmbientColor;
	pObj->m_nVisAreaStencilRef = rParams.nVisAreaStencilRef;

	pObj->m_bHasShadowCasters = false;
	if (GetCVars()->e_Shadows && rParams.m_ShadowMapCasters)
	{
		if (!pOD)
			pOD = pRend->EF_GetObjData(pObj, true);
		if (pOD)
		{
			pOD->m_ShadowCasters = rParams.m_ShadowMapCasters;
			pObj->m_bHasShadowCasters = true;
		}
	}

  pObj->m_ObjFlags |= FOB_INSHADOW;
  pObj->m_fAlpha = rParams.fAlpha;
  pObj->m_DissolveRef = rParams.nDissolveRef;

  ////////////////////////////////////////////////////////////////////////////////////////////////////
  // Process bending
  ////////////////////////////////////////////////////////////////////////////////////////////////////

	if (pRenderNode && pRenderNode->GetRndFlags() & ERF_RECVWIND)
	{
		pRenderNode->m_pRNTmpData->userData.m_fMainBendingScale = rParams.pRenderNode->GetBending();
		SetupBending(pObj, pRenderNode, true);
	}

  ////////////////////////////////////////////////////////////////////////////////////////////////////
  // Set render quality
  ////////////////////////////////////////////////////////////////////////////////////////////////////

  pObj->m_nRenderQuality = (uint16)(rParams.fRenderQuality * 65535.0f);
  pObj->m_fDistance =	rParams.fDistance; 

  pObj->m_DynLMMask[m_nRenderThreadListID] = rParams.nDLightMask;

  {
    //clear, when exchange the state of pLightMapInfo to NULL, the pObj parameters must be update...
    pObj->m_nSort = fastround_positive(rParams.fDistance*2.0f);
  }

  ////////////////////////////////////////////////////////////////////////////////////////////////////
  // Add render elements
  ////////////////////////////////////////////////////////////////////////////////////////////////////

  if (rParams.pMaterial)
    pMaterial = rParams.pMaterial;

  // prepare multi-layer stuff to render object
  if( !rParams.nMaterialLayersBlend && rParams.nMaterialLayers ) 
  {
    uint8 nFrozenLayer = ( rParams.nMaterialLayers&MTL_LAYER_FROZEN )? MTL_LAYER_FROZEN_MASK: 0;
    uint8 nWetLayer = ( rParams.nMaterialLayers&MTL_LAYER_WET )? MTL_LAYER_WET_MASK: 0;
    pObj->m_nMaterialLayers = (uint32) (nFrozenLayer<< 24) | (nWetLayer<<16);
  }
  else
    pObj->m_nMaterialLayers = rParams.nMaterialLayersBlend;

  if(rParams.pTerrainTexInfo && (rParams.dwFObjFlags & (FOB_BLEND_WITH_TERRAIN_COLOR/* | FOB_AMBIENT_OCCLUSION*/)))
  {
    pObj->m_nTextureID = rParams.pTerrainTexInfo->nTex0;
    //pObj->m_nTextureID1 = rParams.pTerrainTexInfo->nTex1;
    if (!pOD)
      pOD = pRend->EF_GetObjData(pObj, true);
    pOD->m_fTempVars[0] = rParams.pTerrainTexInfo->fTexOffsetX;
    pOD->m_fTempVars[1] = rParams.pTerrainTexInfo->fTexOffsetY;
    pOD->m_fTempVars[2] = rParams.pTerrainTexInfo->fTexScale;
    pOD->m_fTempVars[3] = rParams.pTerrainTexInfo->fTerrainMinZ - GetCamera().GetPosition().z;
    pOD->m_fTempVars[4] = rParams.pTerrainTexInfo->fTerrainMaxZ - GetCamera().GetPosition().z;
  }
	
	if( rParams.nCustomData || rParams.nCustomFlags)
	{
		if (!pOD)
			pOD = pRend->EF_GetObjData(pObj, true);

		pOD->m_nCustomData = rParams.nCustomData;
		pOD->m_nCustomFlags = rParams.nCustomFlags;

		if(rParams.nCustomFlags & COB_CLOAK_HIGHLIGHT)
		{
			pOD->m_fTempVars[5] = rParams.fCloakHighlightStrength;
		}
	}

  if( rParams.pFoliage )
    pObj->m_ObjFlags |= FOB_VEGETATION;

	if(rParams.nAfterWater)
		pObj->m_ObjFlags |= FOB_AFTER_WATER;
	else
		pObj->m_ObjFlags &= ~FOB_AFTER_WATER;

  pObj->m_pRenderNode = rParams.pRenderNode;
  pObj->m_pCurrMaterial = pMaterial;

	if (Get3DEngine()->IsTessellationAllowed(pObj))
	{
		// Allow this RO to be tessellated, however actual tessellation will be applied if enabled in material
		pObj->m_ObjFlags |= FOB_ALLOW_TESSELLATION;
	}
	pROM->nRenderList = rParams.nRenderList;
}

//////////////////////////////////////////////////////////////////////////
bool CStatObj::RenderDebugInfo( CRenderObject *pObj, const SRenderObjectModifier *pROM )
{
#ifndef _RELEASE

	if(Get3DEngine()->IsRenderingIntoShadowMap() || m_nRenderStackLevel)
		return false;

  int nThisLod = 0;
  if(m_pLod0)
  {
    if(m_pLod0->m_pLODs)
      for(int i=0; i<MAX_STATOBJ_LODS_NUM; i++)
        if(m_pLod0->m_pLODs[i] == this)
      {
        nThisLod = i;
        break;
      }
  }

  IRenderer * pRend = GetRenderer();
  IMaterial * pMaterial = pObj->m_pCurrMaterial;

	IRenderAuxGeom *pAuxGeom = GetRenderer()->GetIRenderAuxGeom();

	Matrix34 tm;
  if(pROM && pROM->nMatricesInUse)
    tm = pROM->mat;
  else
    tm = pObj->m_II.m_Matrix;

	AABB bbox(m_vBoxMin,m_vBoxMax);
	
	bool bOnlyBoxes = GetCVars()->e_DebugDraw == -1;

	if (GetCVars()->e_DebugDraw == 10)
	{
		SGeometryDebugDrawInfo dd;
		dd.tm = tm;
		if (m_pRenderMesh)
			m_pRenderMesh->DebugDraw( dd );
		return true;
	}
	if ((GetCVars()->e_DebugDraw == 11 || GetCVars()->e_DebugDraw == 12))
	{
#ifdef USE_OCCLUSION_PROXY
		string shortName = PathUtil::GetFile(m_szFileName);
		float color[4] = {1,1,1,1};
		IRenderMesh *pRenderMeshOcclusion = m_pRenderMeshOcclusion;
		if (m_pLod0)
			pRenderMeshOcclusion = m_pLod0->m_pRenderMeshOcclusion;
		if (pRenderMeshOcclusion)
		{
			SGeometryDebugDrawInfo dd;
			dd.tm = tm;
			dd.bNoCull = true;
			pRenderMeshOcclusion->DebugDraw( dd );
			int nTris = pRenderMeshOcclusion->GetIndicesCount()/3;
			pRend->DrawLabelEx( tm.GetTranslation(),1.3f,color,true,true,"%s\n%d",shortName.c_str(),nTris);
		}
#endif
		if (GetCVars()->e_DebugDraw == 12)
			return true;
		else
			return false;
	}

	if (GetCVars()->e_DebugDraw == 1 || bOnlyBoxes)
	{
		if (!m_bMerged)
			pAuxGeom->DrawAABB( bbox,tm,false,ColorB(0,255,255,128),eBBD_Faceted );
		else
			pAuxGeom->DrawAABB( bbox,tm,false,ColorB(255,200,0,128),eBBD_Faceted );
	}

	/*
	// scaled bbox (frustum culling check)
	if(GetCVars()->e_DebugDraw!=8)
  if (GetCVars()->e_DebugDraw > 2 && !bOnlyBoxes)
  {
		AABB bbox(m_vBoxMin,m_vBoxMax);
		bbox.min *= 1.5f;
		bbox.max *= 1.5f;
		bbox.SetTransformedAABB(tm,bbox);
		pAuxGeom->DrawAABB( bbox,false,ColorB(255,0,255,128),eBBD_Faceted );
  }
	*/

	int e_DebugDraw = GetCVars()->e_DebugDraw;
	bool bNoText = e_DebugDraw < 0;
	if (e_DebugDraw < 0 && e_DebugDraw != 1)
		e_DebugDraw = -e_DebugDraw;

	if (m_nRenderTrisCount > 0 && !bOnlyBoxes)
	{ // cgf's name and tris num

		int _nMaxUsableLod = (m_pLod0) ? m_pLod0->m_nMaxUsableLod : m_nMaxUsableLod;
		int nRealNumLods = (m_pLod0) ? m_pLod0->m_nLoadedLodsNum : m_nLoadedLodsNum;
		int nNumLods = nRealNumLods;
		if (nNumLods > _nMaxUsableLod+1)
			nNumLods = _nMaxUsableLod+1;
		int nLod = nThisLod+1;
		if (nLod > nNumLods)
			nLod = nNumLods;

		Vec3 pos = tm.TransformPoint((m_vBoxMin+m_vBoxMax)*0.5f);
		float color[4] = {1,1,1,1};
		int nMats = m_pRenderMesh ? m_pRenderMesh->GetChunks().Count() : 0;
		int nRenderMats=0;

		if(nMats)
		{
			for(int i=0;i<nMats;++i)
			{	
				CRenderChunk &rc = m_pRenderMesh->GetChunks()[i];
				if(rc.pRE && rc.nNumIndices && rc.nNumVerts && ((rc.m_nMatFlags&MTL_FLAG_NODRAW)==0))
					++nRenderMats;
			}
		}

		switch (e_DebugDraw)
		{
		case 1:
			{
				const char *shortName = "";
				if (!m_szGeomName.empty())
					shortName = m_szGeomName.c_str();
				else
					shortName = PathUtil::GetFile(m_szFileName.c_str());
				if (nNumLods > 1)
					pRend->DrawLabelEx( pos,1.3f,color,true,true,"%s\n%d (LOD %d/%d)",shortName,m_nRenderTrisCount,nLod,nNumLods );
				else
					pRend->DrawLabelEx( pos,1.3f,color,true,true,"%s\n%d",shortName,m_nRenderTrisCount );
			}
			break;

		case 2:
			{
				//////////////////////////////////////////////////////////////////////////
				// Show colored poly count.
				//////////////////////////////////////////////////////////////////////////
				int fMult = 1;
				int nTris = m_nRenderTrisCount;
				ColorB clr = ColorB(255,255,255,255);
				if (nTris >= 20000*fMult)
					clr = ColorB(255,0,0,255);
				else if (nTris >= 10000*fMult)
					clr = ColorB(255,255,0,255);
				else if (nTris >= 5000*fMult)
					clr = ColorB(0,255,0,255);
				else if (nTris >= 2500*fMult)
					clr = ColorB(0,255,255,255);
				else if (nTris > 1250*fMult)
					clr = ColorB(0,0,255,255);
				else
					clr = ColorB(nTris/10,nTris/10,nTris/10,255);

				if (pMaterial)
					pMaterial = GetMatMan()->GetDefaultHelperMaterial();
				if (pObj)
				{
					pObj->m_II.m_AmbColor = ColorF(clr.r/155.0f,clr.g/155.0f,clr.b/155.0f,1);
					pObj->m_nMaterialLayers = 0;
					pObj->m_ObjFlags |= FOB_SELECTED;
				}
				
				if (!bNoText)
					pRend->DrawLabelEx( pos,1.3f,color,true,true,"%d",m_nRenderTrisCount );

				return false;
				//////////////////////////////////////////////////////////////////////////
			}
		case 3:
			{
				//////////////////////////////////////////////////////////////////////////
				// Show Lods
				//////////////////////////////////////////////////////////////////////////
				ColorB clr;
				if (nNumLods < 2)
				{
					if (m_nRenderTrisCount <= GetCVars()->e_LodMinTtris  || nRealNumLods > 1)
					{
						clr = ColorB(50,50,50,255);
					}
					else
					{
						clr = ColorB(0,0,0,255);
						float fAngle = gEnv->pTimer->GetFrameStartTime().GetPeriodicFraction(1.0f)*gf_PI2;
						clr.r = 127 + (int)(sinf(fAngle)*120);	// flashing color
					}
				}
				else
				{
					if (nLod == 1)
						clr = ColorB(255,255,0,255);
					else if (nLod == 2)
						clr = ColorB(0,255,0,255);
					else if (nLod == 3)
						clr = ColorB(0,0,255,255);
					else if (nLod == 4)
						clr = ColorB(0,255,255,255);
					else
						clr = ColorB(255,255,255,255);
				}

				if (pMaterial)
					pMaterial = GetMatMan()->GetDefaultHelperMaterial();
				if (pObj)
				{
					pObj->m_II.m_AmbColor = ColorF(clr.r/155.0f,clr.g/155.0f,clr.b/155.0f,1);
					pObj->m_nMaterialLayers = 0;
					pObj->m_ObjFlags |= FOB_SELECTED;
				}

        bool bRenderNodeValid(pObj && pObj->m_pRenderNode && ((int)(void*)(pObj->m_pRenderNode)>0));

        int nMaxUsableLod = min((int)m_nMaxUsableLod, GetCVars()->e_LodMax);

				if (nNumLods > 1 && !bNoText)
        {
          IRenderNode *pRN = (IRenderNode *)pObj->m_pRenderNode;
          pRend->DrawLabelEx( pos,1.3f,color,true,true,"%d/%d (%d/%.1f/%d)",
            nLod, nNumLods, 
            bRenderNodeValid ? pRN->GetLodRatio() : -1, pObj->m_fDistance, nMaxUsableLod);
        }

				return false;
				//////////////////////////////////////////////////////////////////////////
			}
		case 4:
			{
				// Show texture usage.
        if(m_pRenderMesh)
				{
					int nTexMemUsage = m_pRenderMesh->GetTextureMemoryUsage( pMaterial );
					pRend->DrawLabelEx( pos,1.3f,color,true,true,"%d",nTexMemUsage/1024 );		// in KByte
				}
			}
			break;

		case 5:
			{
				//////////////////////////////////////////////////////////////////////////
				// Show Num Render materials.
				//////////////////////////////////////////////////////////////////////////
				ColorB clr(0,0,0,0);
				if (nRenderMats == 1)
					clr = ColorB(0,0,255,255);
				else if (nRenderMats == 2)
					clr = ColorB(0,255,255,255);
				else if (nRenderMats == 3)
					clr = ColorB(0,255,0,255);
				else if (nRenderMats == 4)
					clr = ColorB(255,0,255,255);
				else if (nRenderMats == 5)
					clr = ColorB(255,255,0,255);
				else if (nRenderMats >= 6)
					clr = ColorB(255,0,0,255);
				else if (nRenderMats >= 11)
					clr = ColorB(255,255,255,255);

				if (pMaterial)
					pMaterial = GetMatMan()->GetDefaultHelperMaterial();
				if (pObj)
				{
					pObj->m_II.m_AmbColor = ColorF(clr.r/155.0f,clr.g/155.0f,clr.b/155.0f,1);
					pObj->m_nMaterialLayers = 0;
					pObj->m_ObjFlags |= FOB_SELECTED;
				}

				if (!bNoText)
					pRend->DrawLabelEx( pos,1.3f,color,true,true,"%d",nRenderMats );
			}
			break;
		
		case 6:
			{
				if (pMaterial)
					pMaterial = GetMatMan()->GetDefaultHelperMaterial();
				if (pObj)
				{
					pObj->m_nMaterialLayers = 0;
				}
				
				pRend->DrawLabelEx( pos,1.3f,color,true,true,"%d,%d,%d,%d",
					(int)(pObj->m_II.m_AmbColor.r*255.0f),(int)(pObj->m_II.m_AmbColor.g*255.0f),(int)(pObj->m_II.m_AmbColor.b*255.0f),(int)(pObj->m_II.m_AmbColor.a*255.0f)
					);
			}
			break;
		
		case 7:
      if(m_pRenderMesh)
			{
				int nTexMemUsage = m_pRenderMesh->GetTextureMemoryUsage( pMaterial );
				pRend->DrawLabelEx( pos,1.3f,color,true,true,"%d,%d,%d",m_nRenderTrisCount,nRenderMats,nTexMemUsage/1024 );
			}
			break;
		case 8:
			{
				// Show actual texture usage.
				if(m_pRenderMesh)
				{
					int nTexMemUsage = m_pRenderMesh->GetTextureMemoryUsage( pMaterial );
					pRend->DrawLabelEx( pos,1.3f,color,true,true,"%d",nTexMemUsage/1024 );		// in KByte
				}
			}
			break;
		case 13:
			{
#ifdef SUPPORT_TERRAIN_AO_PRE_COMPUTATIONS
				float fOcclusion = GetOcclusionAmount();
				pRend->DrawLabelEx( pos,1.3f,color,true,true,"%.2f", fOcclusion);
#endif
			}
			break;
		
		case 16:
			{
				// Draw stats for object selected by debug gun
				if( GetRenderer()->IsDebugRenderNode((IRenderNode*)pObj->m_pRenderNode) )
				{
					const char *shortName = PathUtil::GetFile(m_szFileName.c_str());
			
					int texMemUsage = 0;
					
					if(m_pRenderMesh)
					{
						texMemUsage = m_pRenderMesh->GetTextureMemoryUsage( pMaterial );
					}

					pAuxGeom->DrawAABB( bbox,tm,false,ColorB(0,255,255,128),eBBD_Faceted );
				
					float yellow[4] = {1.f,1.f,0.f,1.f};

					const float yOffset = 165.f;
					const float xOffset = 970.f;

					if(m_pParentObject == NULL)
					{					
						pRend->Draw2dLabel(	xOffset, 40.f, 1.5f, yellow, false, "%s", shortName );
									
						pRend->Draw2dLabel(	xOffset, yOffset, 1.5f, color, false, 
															//"Mesh: %s\n"
															"LOD: %d/%d\n"
															"Num Instances: %d\n"
															"Num Tris: %d\n"
															"Tex Mem usage: %.2f kb\n"
															"Mesh Mem usage: %.2f kb\n"
															"Num Materials: %d\n"
															"Mesh Type: %s\n",
															//shortName,
															nLod, nNumLods,
															m_nUsers,
															m_nRenderTrisCount, 
															texMemUsage / 1024.f,
															m_nRenderMeshMemoryUsage / 1024.f,
															nRenderMats,
															m_pRenderMesh->GetTypeName());
					}
					else
					{
						for(int i=0; i<m_pParentObject->SubObjectCount(); i++)
						{
							//find subobject position
							if( m_pParentObject->SubObject(i).pStatObj == this )
							{
								//only render the header once
								if(i==0)
								{
									pRend->Draw2dLabel(	600.f, 40.f, 2.f, yellow, false, "Debug Gun: %s", shortName );
								}
								float y = yOffset + ((i%4)*150.f);
								float x = xOffset - (floor(i/4.f)*200.f);
					
								pRend->Draw2dLabel(	x, y, 1.5f, color, false, 
															"Sub Mesh: %s\n"
															"LOD: %d/%d\n"
															"Num Instances: %d\n"
															"Num Tris: %d\n"
															"Tex Mem usage: %.2f kb\n"
															"Mesh Mem usage: %.2f kb\n"
															"Num Materials: %d\n"
															"Mesh Type: %s\n",
															m_szGeomName.c_str() ? m_szGeomName.c_str() : "UNKNOWN",
															nLod, nNumLods,
															m_nUsers,
															m_nRenderTrisCount, 
															texMemUsage / 1024.f,
															m_nRenderMeshMemoryUsage / 1024.f,
															nRenderMats,
															m_pRenderMesh->GetTypeName());

								break;
							}
						}
					}
				}
			}
			break;

		case 19:	// Displays the triangle count of physic proxies.
			if (!bNoText)
			{
				int nPhysTrisCount = 0;
				for(int j = 0; j < MAX_PHYS_GEOMS_TYPES; ++j)
				{
					if (GetPhysGeom(j))
						nPhysTrisCount += GetPhysGeom(j)->pGeom->GetPrimitiveCount();
				}

				if (nPhysTrisCount == 0)
					color[3] = 0.1f;

				pRend->DrawLabelEx( pos,1.3f,color,true,true,"%d",nPhysTrisCount );
			}
			return false;

		case 22:
			{
				// Show texture usage.
				if(m_pRenderMesh)
				{
					pRend->DrawLabelEx( pos,1.3f,color,true,true,"[LOD %d: %d]", nLod, m_pRenderMesh->GetVerticesCount() );
				}
			}
			break;
		}
	}

	if (GetCVars()->e_DebugDraw == 15 && !bOnlyBoxes)
	{
		// helpers
		for (int i = 0; i < (int)m_subObjects.size(); i++)
		{
			SSubObject *pSubObject = &(m_subObjects[i]);
			if (pSubObject->nType == STATIC_SUB_OBJECT_MESH && pSubObject->pStatObj)
				continue;

			// make object matrix
			Matrix34 tMat = tm * pSubObject->tm;
			Vec3 pos = tMat.GetTranslation();

			// draw axes 
			float s = 0.02f;
      ColorB col(0,255,255,255);
			pAuxGeom->DrawAABB( AABB( Vec3(-s,-s,-s),Vec3(s,s,s) ),tMat,false,col,eBBD_Faceted );      
      pAuxGeom->DrawLine(pos + s*tMat.GetColumn1(), col, pos + 3.f*s*tMat.GetColumn1(), col);
                  
			// text
			float color[4] = {0,1,1,1};
			pRend->DrawLabelEx( pos, 1.3f, color,true,true, "%s", pSubObject->name.c_str( ));
		}
	}

#endif //_RELEASE
	return false;
}

//
// StatObj functions.
//

float CStatObj::GetExtent(EGeomForm eForm)
{
	if (m_subObjects.empty())
	{
		return m_pRenderMesh ? m_pRenderMesh->GetExtent(eForm) : 0;
	}
	CGeomExtent& ext = m_Extents.Make(eForm);
	if (!ext)
	{
		// Create parts for sub-objects.
		if (m_pRenderMesh)
			ext.AddPart( m_pRenderMesh->GetExtent(eForm) );

		// Evaluate sub-objects.
		int nSubCount = m_subObjects.size();
		ext.ReserveParts(nSubCount);
		for (int i = 0; i < nSubCount; i++)
		{
			IStatObj::SSubObject* pSub = &m_subObjects[i];
			if (pSub->nType==STATIC_SUB_OBJECT_MESH && pSub->pStatObj)
				ext.AddPart(pSub->pStatObj->GetExtent(eForm));
			else
				ext.AddPart(0.f);
		}
		ext.TrimParts();
	}
	return ext.TotalExtent();
}

int CStatObj::GetMinUsableLod() 
{ 
  return clamp_tpl(GetCVars()->e_LodMin,(int)m_nMinUsableLod0,(int)m_nMaxUsableLod0);
}

int CStatObj::ComputeLod(Matrix34 & mat, float fEntDistance, IRenderNode * pObj, bool bForPrecache)
{
	float fScale = mat.GetColumn0().GetLengthFloat();			//Parent scale

	float fLodRatio;
	bool bFoliage;

	if(pObj)
	{
		fLodRatio = pObj->GetLodRatioNormalized();
		bFoliage = (pObj->GetFoliage() != 0); 
	}
	else
	{
		fLodRatio = 1.0f;
		bFoliage = false;
	}

	return ComputeLodFromScale(fScale, fLodRatio, fEntDistance, bFoliage, bForPrecache);
}

int CStatObj::ComputeLodFromScale(float fScale, float fLodRatioNormalized, float fEntDistance, bool bFoliage, bool bForPrecache)
{
	assert(!m_pLod0);

	int nNewLod = 0;

	nNewLod = CObjManager::GetObjectLOD(fEntDistance, fLodRatioNormalized, GetRadius()*fScale);

	int nMinLod = GetMinUsableLod();
	nNewLod += nMinLod;
	int nMaxUsableLod = min( (int)m_nMaxUsableLod, GetCVars()->e_LodMax );
	if(!nMaxUsableLod && bForPrecache)
		nMaxUsableLod = 1;
	nNewLod = bFoliage ? 0 : CLAMP(nNewLod, 0, nMaxUsableLod );

	return nNewLod;
}

//////////////////////////////////////////////////////////////////////////
void CStatObj::DebugDraw( const SGeometryDebugDrawInfo &info, float fExtrdueScale )
{
	int nFlags = 0;
	if (info.bNoLines)
	{
		nFlags |= BIT(1);
	}
	if (info.bNoCull)
	{
		nFlags |= BIT(0);
	}

	if (m_nFlags & STATIC_OBJECT_COMPOUND && !m_bMerged)
		{
			// Draw sub objects.
			for (int i = 0; i < (int)m_subObjects.size(); i++)
			{
				if (!m_subObjects[i].pStatObj || m_subObjects[i].bHidden || m_subObjects[i].nType != STATIC_SUB_OBJECT_MESH)
					continue;

				SGeometryDebugDrawInfo subInfo = info;
				subInfo.tm = info.tm * m_subObjects[i].tm;
				m_subObjects[i].pStatObj->DebugDraw( subInfo, fExtrdueScale );
			}
		}
	else if (m_pRenderMesh)
	{
		m_pRenderMesh->DebugDraw( info, ~0 , fExtrdueScale );
	}
	else
	{
		// No RenderMesh in here, so probably no geometry in highest LOD, find it in lower LODs
    if(m_pLODs)
		  for(int nLod = 0; nLod<=(int)m_nMaxUsableLod; nLod++)
		{
			assert(nLod<MAX_STATOBJ_LODS_NUM);
			if(m_pLODs[nLod] && m_pLODs[nLod]->m_pRenderMesh)
			{
				m_pLODs[nLod]->m_pRenderMesh->DebugDraw( info, ~0 , fExtrdueScale );
				break;
			}
		}
	}
}

void CStatObj::RenderRenderMesh(CRenderObject * pRenderObject, SInstancingInfo * pInstInfo, int nThreadId, const SRenderObjectModifier * pROII)
{
	if (GetCVars()->e_DebugDraw && (!GetCVars()->e_DebugDrawShowOnlyCompound || (m_bSubObject || m_pParentObject)))
	{
		int nLod=0;
		if(m_pLod0 && m_pLod0->m_pLODs)
			for(; nLod<MAX_STATOBJ_LODS_NUM; nLod++)
			{
				if(m_pLod0->m_pLODs[nLod] == this)
				{ 
					m_pRenderMesh->SetMeshLod(nLod);  
					break; 
				}
			}

			if(GetCVars()->e_DebugDrawShowOnlyLod >= 0)
				if(GetCVars()->e_DebugDrawShowOnlyLod != nLod)
					return;

			if (RenderDebugInfo(pRenderObject,pROII))
				return;
	}

	if (!Get3DEngine()->IsRenderingIntoShadowMap())
	{
		if (GetCVars()->e_StreamCgfDebug==1)
		{
			RenderStreamingDebugInfo(pRenderObject);
		}

		if (GetCVars()->e_CoverCgfDebug==1)
		{
			RenderCoverInfo(pRenderObject);
		}
	}


	if(GetCVars()->e_StatObjTestOBB && !Get3DEngine()->IsRenderingIntoShadowMap())
	{
		Matrix34 worldTM;
		if(pROII && pROII->nMatricesInUse)
			worldTM = pROII->mat;
		else
			worldTM = pRenderObject->GetMatrix();

		OBB obb = OBB::CreateOBBfromAABB(Matrix33(worldTM), AABB(m_vBoxMin,m_vBoxMax));

		if(!GetCamera().IsOBBVisible_E(worldTM.GetTranslation(), obb))
		{
			return;
		}
	}

	if(m_pRenderMesh)
		m_pRenderMesh->Render(pRenderObject, pROII);
}
